-- Base Function

ShowDebugPrint = false
fn DebugPrint msg = 
(
	if ShowDebugPrint then
	(
		print ("[Debug]" + msg)
	)
)

/*
fn Fn_Round num = 
(
	remainder = num- (floor num)
	if (remainder >= .5) then (fnum= ceil num) else (fnum = floor num) 
	fnum
)
*/

fn Fn_Clamp num cMin cMax = 
(
	local result = num
	if result < cMin then
	(
		result = cMin
	)
	else
	(
		if result > cMax then 
		(
			result = cMax
		)
	)
	return result
)

fn IsInRange num cMin cMax = 
(
	local result = false
	if ((num >= cMin) and (num <= cMax)) then
	(
		result = true
	)
	return result
)

/*
fn Fn_ClampVector V vMin vMax =
(
	tempvector=[0.0,0.0,0.0] 
	for a=1 to 3 do (
		tempvector[a]=clamp V[a] vMin vMax
	)
	tempvector
)
	
fn Fn_AbsoluteValVector V =
(
	tempvector=[0.0,0.0,0.0] 
	for a=1 to 3 do (
		tempvector[a]=abs V[a] 
	)
	tempvector
)
*/

-- A2U_Function
struct A2U_FbxParams
(
	FileVersion = "FBX201600",
	ASCII = "false",

	SmoothingGroups = "true",
	NormalsPerPoly = "false",
	TangentSpaceExport = "true",
	GeomAsBone = "true",
	Triangulate= "true",
	PreserveEdgeOrientation = "true",

	Resampling = "60.0",
	Skin = "true",
	Shape = "true",

	Cameras = "false",
	Lights = "false",

	EmbedTextures = "false",
	CAT2HIK = "false",
	UpAxis = "Z"
)

fn A2UFn_IsBoneOrBiped obj = 
(
	if (isKindOf obj Biped_Object) or (isKindOf obj BoneGeometry) or (isKindOf obj helper) or ((isKindOf obj shape) and obj.boneEnable == true) or ((isKindOf obj GeometryClass) and obj.boneEnable == true) then
		return true
	else
		return false
)

fn A2UFn_IsGeoBone obj = 
(
	if (isKindOf obj BoneGeometry) then
		return true
	else
		return false
)

fn A2UFn_IsBiped obj = 
(
	if (isKindOf obj Biped_Object) then
		return true
	else
		return false
)

-- 所有 的helper
-- 开启boneenable 的shape
-- 开启boneenable 的Geometry
fn A2UFn_IsDummyBone obj = 
(
	if (isKindOf obj helper) or ((isKindOf obj shape) and obj.boneEnable == true) or ((isKindOf obj GeometryClass) and obj.boneEnable == true) then
		return true
	else
		return false
)

struct A2U_AnimClipParams
(
	AnimName,
	AnimRangeStart,
	AnimRangeEnd
)

fn A2UFn_GetAnimClips =
(
	local animClipArray = #()

	local ttCount = FrameTagManager.GetTagCount()
	if ttCount > 0 then
	(
		for i = 1 to ttCount do
		(
			local str_tt = FrameTagManager.GetNameByID i
			if matchPattern str_tt pattern:"*#*" then
			(
				local a_tt = (filterString str_tt "#")
				local animName = a_tt[1]
				local animRangeStart = FrameTagManager.GetTimeByID i
				local animRangeEnd = a_tt[2] as time
				if animRangeStart < animRangeEnd then
				(
					local animClip = A2U_AnimClipParams AnimName:animName AnimRangeStart:animRangeStart AnimRangeEnd:animRangeEnd
					append animClipArray animClip
				)
			)
		)
	)

	return animClipArray
)

fn A2UFn_IsNotExport obj =
(
	local IsNotExport = getUserProp obj "aurogon_fbx_export_as" == "noexport"
	
	-- 在Gujian4环境下 *origin 不导出
	if A2UFn_IsWorkSpace_Gujian4() and (obj.name == "*origin") then
		IsNotExport = true
	
	-- *skelid 不导出
	if (obj.name == "*skelid") then
		IsNotExport = true
	
	return IsNotExport
)

fn A2UFn_IsExportSkeletalMesh geo skelName = 
(
	local IsCloth = matchPattern geo.name pattern:"*#cloth*"
	local IsSkelPlaneMesh = (geo.name == skelName)	-- 骨架自带面片 不导出
	local IsNotExport = A2UFn_IsNotExport geo
	local IsBoneOrBiped = A2UFn_IsBoneOrBiped geo
	local IsHaveSkinModifier = geo.modifiers[Skin] != undefined
	local IsNamingValid = findstring geo.name "SK_" != undefined

	-- note: 按需扩展
	-- 目前导出规则：
	-- 名字带 "SK_"
	-- 包含Skin修改器
	-- not 隐藏
	-- not 属性为不导出
	-- not 布料（布料需要跟着其他geo一起导出）
	-- not 骨架名box
	-- not Bone/Biped物体
	-- 是 mesh/poly/polymeshobject(带有修改器的mesh) 的一种

	return not geo.isHidden and IsHaveSkinModifier and IsNamingValid and
			not IsNotExport and not IsCloth and not IsSkelPlaneMesh and not IsBoneOrBiped and
			(iskindof geo editable_mesh or iskindof geo editable_poly or iskindof geo polymeshobject)
)

fn A2UFn_ReplaceNodeName curNode sourceString replaceString = 
(
	local oldName = curNode.name
	local newName = substituteString oldName sourceString replaceString
	print(newName)
	curNode.name = newName
)

fn A2UFn_GenerateSMFilename bAll =
(
	local objs = selection
	if bAll then (objs = geometry)

	for obj in objs do 
	(
		if (isKindOf obj GeometryClass) then
		(
			if not obj.isHidden and findstring obj.name "_" != undefined and
				--not IsNothing and not IsCloth and not IsSkelPlaneMesh and
				(iskindof obj editable_mesh or iskindof obj editable_poly or iskindof obj polymeshobject) then
			(
				return obj.name
			)
		)
	)

	return ""
)

-- 选相关联的布料代理模型（隐藏的不选择）
fn A2UFn_SelectRelateClothMesh ingeo = 
(
	selectMore ingeo

	for geo in geometry do 
	(
		local geoName = ingeo.name
		local strClothMatch = ingeo.name + "#cloth*"

		if (matchPattern geo.name pattern:strClothMatch) and (not geo.isHidden) then
		(
			selectMore geo
		)
	)
)

fn TraversalGetChildNodes curNode childNodesArray = 
(
	if curNode == undefined then
	(
		return false
	)

	for childNode in curNode.children do
	(
		if(TraversalGetChildNodes childNode childNodesArray == false) then return false
	)
	
	append childNodesArray curNode
	return true
)

fn __traversalChildNode parentNode selectionArray = 
(
	if parentNode == undefined then
	(
		return false
	)

	for childnode in parentNode.children do
	(
		if childnode.children.count > 0 then
		(
			if not (__traversalChildNode childnode selectionArray) then
			(
				return false
			)
		)
		else
		(
			append selectionArray childnode
			print(childnode.name)
		)
	)

	append selectionArray parentNode
	print(parentNode.name)
	
	return true
)

fn __traversalChildSkelNode parentNode selectionArray = 
(
	if parentNode == undefined then
	(
		--hlog_error("hfn_selectSkeletonAll: parentNode is undefined")
		return false
	)

	for childnode in parentNode.children do
	(
		if childnode.children.count > 0 then
		(
			if not (__traversalChildSkelNode childnode selectionArray) then
				return false
		)
		else
		(
			if not (A2UFn_IsNotExport childnode) and (A2UFn_IsBoneOrBiped childNode) then
			(
				append selectionArray childnode
			)
		)
	)

	-- handle the parent node
	if not (A2UFn_IsNotExport parentNode) and (A2UFn_IsBoneOrBiped parentNode) then
	(
		append selectionArray parentNode
	)
	
	return true
)

fn A2UFn_IsBip01RootObject node = 
(
	-- if node == undefined then return false
	-- local ret = false
	-- if ClassOf(node) == Biped_Object and ClassOf(node.Controller) == Vertical_Horizontal_Turn then
	-- 	ret = true
	-- return ret
	if node == undefined then return false
	return node.name == "Bip01"
)

fn A2UFn_IsSkelRootObject node = 
(
	if node == undefined then return false
	local ret = false
	if ClassOf(node) == Dummy and node.name == "Root" then
	(
		for _child in node.children do
		(
			if _child.name == "Bip01" then
				ret = true
		)
	)
	return ret
)

fn A2UFn_IsOriginNodeObject node = 
(
	if node == undefined then return false
	local ret = false
	if ClassOf(node) == Dummy and node.name == "*origin" then
	(
		local _nodeParent = node.parent
		if A2UFn_IsSkelRootObject(_nodeParent) then
			ret = true
	)
	return ret
)

fn A2UFn_IsCamera node = 
(
	return (findItem ($Cameras as Array) node > 0) and (isKindOf node camera)
)

fn A2UFn_IsCameraTarget node = 
(
	return (findItem ($Cameras as Array) node > 0) and (not (isKindOf node camera))
)

fn A2UFn_IsCameraOrCameraTarget node = 
(
	return (findItem ($Cameras as Array) node > 0)
)

fn A2UFn_GetAllBipedObject = 
(
	local bipedObjects = #()
	for geo in geometry where (isKindOf geo Biped_Object) do
	(
		append bipedObjects geo
	)
	return bipedObjects
)

fn A2UFn_GetAllBipedRootObject = 
(
	local bipedRootObjects = #()
	for geo in geometry where ((isKindOf geo Biped_Object) and (classOf geo.controller == Vertical_Horizontal_Turn)) do
	(
		append bipedRootObjects geo
	)
	return bipedRootObjects
)

fn A2UFn_GetAllBoneObject = 
(
	local boneObjects = #()
	for geo in geometry where (isKindOf geo BoneGeometry) do
	(
		append boneObjects geo
	)
	return boneObjects
)

fn A2UFn_GetAllShape = 
(
	return shapes
)

fn A2UFn_GetAllShapeAsBone = 
(
	local shapeAsBone = #()
	for curShape in shapes where curShape.boneEnable == true do
	(
		append shapeAsBone curShape
	)
	return shapeAsBone
)

fn A2UFn_GetAllHelper = 
(
	return helpers
)

fn A2UFn_GetAllHelperExcludeParticleView = 
(
	local helperArray = #()
	for _node in helpers where (classOf _node != Particle_View) do
	(
		append helperArray _node
	)
	return helperArray
)

fn A2UFn_GetAllMesh = 
(
	local meshObjects = #()
	for geo in geometry where not (isKindOf geo Biped_Object) do
	(
		append meshObjects geo
	)
	return meshObjects
)

fn A2UFn_GetAllSkelRoot = 
(
	local rootObjects = #()
	for obj in helpers do
	(
		if (obj.name == "root" or obj.name == "Root") and obj.parent == undefined then 
			append rootObjects obj
	)
	return rootObjects
)

fn A2UFn_GetAllBip01Root = 
(
	local biped01Roots = #()
	for obj in geometry do
		if obj.name == "Bip01" then append biped01Roots obj
	end
	for obj in helpers do
		if obj.name == "Bip01" then append biped01Roots obj
	end
	return biped01Roots
)

fn A2UFn_GetAllChildNodes parentObj = 
(
	local selectionArray = #()
	local ret = __traversalChildNode parentObj selectionArray
	if ret then
	(
		return selectionArray
	)
	return #()
)

fn A2UFn_GetAllSkeleton skelRoot = 
(
	local selectionArray = #()
	local ret = __traversalChildSkelNode skelRoot selectionArray
	if ret then
	(
		return selectionArray
	)
	return #()
)

-- return All Skeleton Nodes
fn A2UFn_SelectAllSkeleton skelRoot = 
(
	local selectionArray = #()
	local ret = __traversalChildSkelNode skelRoot selectionArray
	if ret then
	(
		selectMore selectionArray
	)
	return ret
)

fn GetSkelAllSkeletonBipedBones root = 
(
	local nodes = A2UFn_GetAllSkeleton root
	local bipedObjects = #()
	for node in nodes where (isKindOf node Biped_Object) do
	(
		append bipedObjects node
	)
	return bipedObjects
)

fn GetSkelAllSkeletonNormalBones root = 
(
	local nodes = A2UFn_GetAllSkeleton root
	local normalBoneObjects = #()
	for node in nodes do
	(
		append normalBoneObjects node
	)
	return normalBoneObjects
)

fn A2UFn_GetAllOriginObjects = 
(
	local rootObjects = A2UFn_GetAllSkelRoot()
	local originObjects = #()
	for rootObj in rootObjects do
	(
		for _obj in rootObj.children do
		(
			if (classOf _obj == Dummy) and (_obj.name == "*origin") then
			(
				append originObjects _obj
			)
		)
	)
	return originObjects
)

fn A2UFn_GetAllIDObjects = 
(
	local rootObjects = A2UFn_GetAllSkelRoot()
	local idObjects = #()
	for rootObj in rootObjects do
	(
		for _obj in rootObj.children do
		(
			if (classOf _obj == Text) and (_obj.name == "*skelid") then
			(
				append idObjects _obj
			)
		)
	)
	return idObjects
)

fn A2UFn_GetBip01RootBySkelRoot root = 
(
	local bipRootNode = undefined
	local counter = 0
	local allBones = A2UFn_GetAllSkeleton root
	for _obj in allBones do
	(
		if A2UFn_IsBip01RootObject(_obj) then
		(
			bipRootNode = _obj
			counter = counter + 1
		)
	)
	if counter > 1 then
	(
		bipRootNode = undefined
		print("错误 一个Root下有多个Bip01点")
	)
	return bipRootNode
)

fn A2UFn_GetOriginNodeBySkelRoot root = 
(
	local originNode = undefined
	for _obj in root.children do
	(
		if (classOf _obj == Dummy) and (_obj.name == "*origin") then
		(
			originNode = _obj
		)
	)
	return originNode
)

fn A2UFn_GetIDNodeBySkelRoot root = 
(
	local skelidNode = undefined
	for _obj in root.children do
	(
		if (classOf _obj == Text) and (_obj.name == "*skelid") then
		(
			skelidNode = _obj
		)
	)
	return skelidNode
)

fn A2UFn_GetSkelIDNameBySkelRoot root = 
(
	local skelidName = ""
	for _obj in root.children do
	(
		if (classOf _obj == Text) and (_obj.name == "*skelid") then
		(
			skelidName = _obj.text
		)
	)
	return skelidName
)

fn A2UFn_CheckSkelIDNameValid skelidName = 
(
	local tokenIndex = findString skelidName "#"
	if tokenIndex == undefined then
	(
		return true
	)
	
	local stringArr = filterString skelidName "#"
	if stringArr.count == 1 then
	(
		return true
	)
	else if stringArr.count == 2 then
	(
		return true
	)
	else
	(
		return false
	)
)

-- return skelName：retArr[1] idName：retArr[2] 
fn A2UFn_GetSkelNameAndIDNameBySkelIDName skelidName &skelName &idName = 
(
	local stringArr = filterString skelidName "#"
	skelName = stringArr[1]
	idName = stringArr[2]
	if skelName == undefined then skelName = ""
	if idName == undefined then idName = ""
)

fn A2UFn_CheckMaxFileNamePrefix suffix = 
(
	local _fileName = (getFilenameFile maxFileName)
	return (findstring _fileName suffix == 1)
)

fn A2UFn_GetModelSkelAndSetName geo = 
(
	local _strarr = filterString geo.name "_"
	local ret = ""
	if _strarr.count >= 4 then
		ret = _strarr[2] + "_" + _strarr[3]
	return ret
)

-- 刀光和Custom动画名 可能需要废弃
fn A2UFn_GetAnimFinalName originalName =
(
	local finalFileName = originalName
	local pos = findString finalFileName "$"
	if pos != undefined then (
		swordLight = true
		finalFileName = replace finalFileName pos 1 ""
	)
	pos = findString finalFileName "%"
	if pos != undefined then (
		useCustom = true
		finalFileName = replace finalFileName pos 1 ""
	)

	return finalFileName
)

fn A2UFn_RotateRoot degree =
(
	if getnodebyname "Root" == undefined then
	(
		messagebox("没有找到Root点")
	)
	else
	(
		$Root.rotation = (eulerAngles 0 0 degree)
	)
)

fn A2UFn_RotateWeapon degree =
(
	local rootBone = $Root
	if rootBone == undefined then
	(
		messagebox("没有找到Root点")
	)
	else if rootBone.children.count <= 0 then
	(
		messagebox("Root点没有子骨骼?")
	)
	else
	(
		for childnode in rootBone.children do
		(
			childnode.rotation = (eulerAngles 0 0 ((quatToEuler childnode.rotation).z + degree))
		)
	)
)

-- 检查是否Skin上面还有其他修改器
fn A2UFn_CheckNoModifierOverSkinModifier geo = 
(
	if geo.modifiers[#Skin] == undefined then
		return true

	-- 顶部的修改器Index为1
	if classOf(geo.modifiers[1]) == Skin then
	(
		return true
	)
	else
	(
		messagebox(geo.name + "的Skin修改器上面有其它修改器")
		return false
	)
)

fn A2UFn_SelectAllMorphNodes = 
(
	local morphNodes = #()
	for geo in geometry do 
	(
		if A2UFn_IsBoneOrBiped geo then
			continue
		local bIsMorphNode = false
		local modifiers = geo.modifiers
		for modi in modifiers do
		(
			if classOf(modi) == Morpher then
			(
				bIsMorphNode = true
			)
		)
		if (bIsMorphNode == true) then
		(
			append morphNodes geo
		)
	)
	return morphNodes
)

fn A2UFn_SelectAllMorphAnimatedNode &onlyOneMorphAnimNames &outRangedMorphAnimNames = 
(
	local morphNodes = A2UFn_SelectAllMorphNodes()
	local animNodes = #()

	for curNode in morphNodes do
	(
		local modi = curNode.modifiers[#Morpher]
		if (modi == undefined) then
			continue
		
		local bIsAnimNode = false
		local validChannels = #()
		for i = 1 to 100 do
		(
			if ((WM3_MC_HasData curNode.morpher i) == true) then
			(
				append validChannels i
			)
		)

		for id in validChannels do
		(
			if modi[id].controller.keys.count > 0 then
			(
				bIsAnimNode = true
				if modi[id].controller.keys.count == 1 then
				(
					local _name = ("【模型】" + curNode.name + "【morph】" + modi[id].name)
					appendIfUnique onlyOneMorphAnimNames _name
				)
				for k in modi[id].controller.keys do
				(
					if k.time < animationRange.start or k.time > animationRange.end then
					(
						local _name = ("【模型】" + curNode.name + "【morph】" + modi[id].name)
						appendIfUnique outRangedMorphAnimNames _name
					)
				)
			)
		)

		if bIsAnimNode then
		(
			append animNodes curNode
		)
	)
	return animNodes
)

fn A2UFn_HaveNotBoneEnableShape rootBone = 
(
	local allSkel = A2UFn_GetAllChildNodes rootBone
	local notBoneEnabledShapes = #()
	for obj in allSkel where isKindOf obj shape do
	(
		if obj.boneEnable == false then
		(
			append notBoneEnabledShapes obj
		)
	)
	return notBoneEnabledShapes
)

-- Root/*origin关键帧数量超过1帧 或 Root/*origin位置旋转不相等
fn A2UFn_NeedCalcRootMotion rootBone = 
(
	local originBone = A2UFn_GetOriginNodeBySkelRoot rootBone
	if rootBone == undefined or originBone == undefined then return false
	
	local ret = false
	if (numkeys originBone.position.controller > 1) or (numkeys originBone.rotation.controller > 1) or (numkeys rootBone.position.controller > 1) or (numkeys rootBone.rotation.controller > 1) then
	(
		ret = true
	)
	else
	(
		if originBone.position != rootBone.position then
			ret = true
		if originBone.rotation != rootBone.rotation then
			ret = true
	)
	return ret
)

-- 返回是否把至少一个*resetlink进行了重置
fn A2UFn_TryResetAllNodeLinksByRoot rootBone = 
(
	local AllBones = A2UFn_GetAllSkeleton rootBone
	local bHaveResetLink = false
	-- 先打开auto key
	for _bone in AllBones do
	(
		if (findString _bone.name "*resetlink") == 1 and (classOf(_bone.controller) == Link_Constraint) then
		(
			for i = 1 to _bone.controller.getNumTargets() do
			(
				_bone.controller.DeleteTarget i
			)
			-- _bone.controller.addworld 0		-- 是否需要 Link to World
			bHaveResetLink = true
		)
	)
	return bHaveResetLink
)

fn A2UFn_ExportSkelAnimTransformData skelRoot fileName = 
(
	local out_file = createfile fileName
	local AllBones = A2UFn_GetAllSkeleton skelRoot

	format "Time Start:%\n" animationRange.start to:out_file
	format "Time End:%\n" animationRange.end to:out_file

	-- 先打开auto key
	for _bone in AllBones do
	(
		format "Bone Name:%\n" _bone.name to:out_file
		for i = animationRange.start to animationRange.end do
		(
			at time i
			(
				-- WORLD COORD --
				local selfMat = _bone.transform
				format "%\n" (selfMat.translationpart as string) to:out_file
				format "%\n" (selfMat.rotationpart as string) to:out_file
				format "%\n" (selfMat.scale as string) to:out_file
				-- format "%\n" (quattoeuler(selfMat.rotationpart) as string) to:out_file
				
				-- LOCAL COORD --
				-- local parentMat = undefined
				-- if (_bone.parent == undefined) then
				-- (
				-- 	parentMat = _bone.transform
				-- )
				-- else
				-- (
				-- 	parentMat = _bone.parent.transform
				-- )
				-- local invParentMat = inverse(parentMat)
				-- local resultMat = selfMat * invParentMat
				-- format "%\n" (resultMat.translationpart as string) to:out_file
				-- format "%\n" (resultMat.rotationpart as string) to:out_file
				-- format "%\n" quattoeuler(resultMat.rotationpart as string) to:out_file
				-- format "%\n" (resultMat.scalepart as string) to:out_file
			)
		)
	)

	close out_file
	print ("Export FBX Transform Data OK")
)

fn A2UFn_ExportSkelOriginMotionData skelOrigin fileName = 
(
	local out_file = createfile fileName
	local out_str = ""
	local animationStart = animationRange.start
	local animationEnd = animationRange.end

	for i = animationStart to animationEnd do
	(
		at time i
		(
			-- Change X/Y Order and Multiply -1 to fit the coord system in UE
			local selfMat = skelOrigin.transform
			local _x = -selfMat.translationpart.y
			_x = (floor _x+0.5) as integer
			local _y = -selfMat.translationpart.x
			_y = (floor _y+0.5) as integer
			local _z = selfMat.translationpart.z
			_z = (floor _z+0.5) as integer
			local _yaw = (quattoeuler selfMat.rotationpart).z * -1
			_yaw = (mod (floor (_yaw+0.5)) 360) as integer
			
			out_str = out_str + _x as string + "," + _y as string + "," + _z as string + "," + _yaw as string
		)
		if i < animationRange.end then
		(
			out_str = out_str + "#"	
		)
	)

	format "%" out_str to:out_file
)

fn A2UFn_GetAllClothMesh = 
(
	local clothMeshes = #()
	for geo in geometry do
	(
		local isClothMesh = matchPattern geo.name pattern:"*#cloth*"
		if isClothMesh then
		(
			append clothMeshes geo
		)
	)
	return clothMeshes
)

fn A2UFn_ApplyMaterialToAllClothMesh = 
(
	local clothMeshes = A2UFn_GetAllClothMesh()
	for geo in clothMeshes do
	(
		local newMat = StandardMaterial()
		newMat.name = geo.name
		geo.material = newMat
	)
)

fn A2UFn_CheckSkinModifierHaveBoneByName skinNode boneName = 
(
	local skinMod = skinNode.modifiers[#Skin]
	if skinMod == undefined then
		return false

	local ret = false
	subobjectLevel = 1
	modPanel.setCurrentObject skinMod
	subobjectLevel = 0
	local boneNum = skinOps.getnumberbones skinMod
	for i = 1 to boneNum do
	(
		local curBoneName = (skinOps.getBoneName skinMod i 0)
  		if curBoneName == boneName then
			ret = true
	)
	return ret
)

fn A2UFn_CheckSkeletonTreeHaveZeroScaleBone skelRoot &zeroScaleBones &zeroScaleBoneNames = 
(
	local zeroLimit = 0.0001
	local allBones = A2UFn_GetAllSkeleton skelRoot

	local bSkeletonTreeHaveZeroScale = false
	for curBone in allBones do
	(
		if (isKindOf curBone Biped_Object) then
		(
			local boneScale = in coordsys world Biped.getTransform curBone #scale
			if (abs boneScale.x <= zeroLimit or abs boneScale.y <= zeroLimit or abs boneScale.z <= zeroLimit) then
			(
				zeroScaleBoneNames = zeroScaleBoneNames + curBone.name + "\n"
				append zeroScaleBones curBone
				bSkeletonTreeHaveZeroScale = true
				print("Zero Scale Bone: " + curBone.name + " value is " + boneScale as string)
			)
		)
		else
		(
			local boneScale = curBone.transform.scale
			if (abs boneScale.x <= zeroLimit or abs boneScale.y <= zeroLimit or abs boneScale.z <= zeroLimit) then
			(
				zeroScaleBoneNames = zeroScaleBoneNames + curBone.name + "\n"
				append zeroScaleBones curBone
				bSkeletonTreeHaveZeroScale = true
				print("Zero Scale Bone: " + curBone.name + " value is " + boneScale as string)
			)
		)
	)
	return bSkeletonTreeHaveZeroScale
)

fn A2UFn_FixSkeletonTreeZeroScaleBone skelRoot &zeroScaleBones &zeroScaleBoneNames = 
(
	local zeroLimit = 0.0001
	local fixValueBase = 0.01
	local allBones = A2UFn_GetAllSkeleton skelRoot

	local bSkeletonTreeHaveZeroScale = false
	for curBone in allBones do
	(
		local boneScale = undefined
		local fixValue = fixValueBase
		if (isKindOf curBone Biped_Object) then
		(
			boneScale = in coordsys world Biped.getTransform curBone #scale
			fixValue = fixValue * 100
		)
		else
		(
			boneScale = curBone.transform.scale
		)
		if (abs boneScale.x <= zeroLimit or abs boneScale.y <= zeroLimit or abs boneScale.z <= zeroLimit) then
		(
			bSkeletonTreeHaveZeroScale = true

			local newBoneScale = boneScale
			if abs boneScale.x <= zeroLimit then
			(
				if boneScale.x >= 0 then
					newBoneScale.x = fixValue
				else
					newBoneScale.x = fixValue * -1
			)
			if abs boneScale.y <= zeroLimit then
			(
				if boneScale.y >= 0 then
					newBoneScale.y = fixValue
				else
					newBoneScale.y = fixValue * -1
			)
			if abs boneScale.z <= zeroLimit then
			(
				if boneScale.z >= 0 then
					newBoneScale.z = fixValue
				else
					newBoneScale.z = fixValue * -1
			)

			-- fix boneScale with newBoneScale
			if (isKindOf curBone Biped_Object) then
			(
				in coordsys world Biped.setTransform curBone #scale newBoneScale false
			)
			else
			(
				curBone.transform.scale = newBoneScale
			)
			
			zeroScaleBoneNames = zeroScaleBoneNames + curBone.name + "\n"
			append zeroScaleBones curBone
		)
	)
	return bSkeletonTreeHaveZeroScale
)

fn A2UFn_GetRootNodeNum = 
(
	local num = 0
	for obj in Helpers do
	(
		if obj.name == "Root" or obj.name == "root" then
			num = num + 1
	)
	return num
)

fn A2UFn_GetRootNodeChildHaveAnyBipedObject = 
(
	
)

fn __CheckZeroScaleKey _Node &ZeroScaleBoneCount &ZeroScaleFrameLog = 
(
	local bHaveZeroScaleFrame = false
	if (hasProperty _Node.controller #scale) then
	(
		if (_Node.controller.scale.keys != undefined and _Node.controller.scale.keys.count > 0) then
		(
			for i = 1 to _Node.controller.scale.keys.count do
			(
				local _scale = _Node.controller.scale.keys[i].value
				if _scale.x == 0 or _scale.y == 0 or _scale.z == 0 then
				(
					print (_Node.name + " has zero scale keys at FRAME:" + _Node.controller.scale.keys[i].time as string)
					ZeroScaleFrameLog = ZeroScaleFrameLog + (_Node.name + " 在第 " + _Node.controller.scale.keys[i].time as string + " 帧为零缩放") + "\n"
					bHaveZeroScaleFrame = true
				)
			)
		)
	)
	else if(classOf(_Node.controller) == Link_Constraint) then
	(
		if (_Node.controller.link_params.scale.keys.count > 0) then
		(
			for i = 1 to _Node.controller.link_params.scale.keys.count do
			(
				local _scale = _Node.controller.link_params.scale.keys[i].value
				if _scale.x == 0 or _scale.y == 0 or _scale.z == 0 then
				(
					print (_Node.name + " has zero scale keys at FRAME:" + _Node.controller.link_params.scale.keys[i].time as string)
					ZeroScaleFrameLog = ZeroScaleFrameLog + (_Node.name + " 在第 " + _Node.controller.link_params.scale.keys[i].time as string + " 帧为零缩放") + "\n"
					bHaveZeroScaleFrame = true
				)
			)
		)
	)
	if bHaveZeroScaleFrame == true then
	(
		ZeroScaleBoneCount = ZeroScaleBoneCount + 1
	)
)

fn A2UFn_CheckZeroScaleSkelNodes skelRoot &ZeroScaleBoneCount &ZeroScaleFrameLog = 
(
	local nodes = A2UFn_GetAllSkeleton(skelRoot)
	for _node in nodes do
	(
		__CheckZeroScaleKey _node &ZeroScaleBoneCount &ZeroScaleFrameLog
	)
)

fn A2UFn_DeleteKeysOutsideRange nodes rangeStart rangeEnd = 
(
	local _start = animationrange.start
	local _end = animationrange.end
	
	for obj in nodes do
	(
		-- Position
		local keys = obj.position.controller.keys
		for i = 1 to keys.count do
		(
			if not (IsInRange keys[i].time _start _end) then
			(
				selectKey obj.position.controller i
			)
		)
		deleteKeys obj.position.controller #selection
		-- Rotation
		keys = obj.rotation.controller.keys
		for i = 1 to keys.count do
		(
			if not (IsInRange keys[i].time _start _end) then
			(
				selectKey obj.rotation.controller i
			)
		)
		deleteKeys obj.rotation.controller #selection
		-- Scale
		keys = obj.scale.controller.keys
		for i = 1 to keys.count do
		(
			if not (IsInRange keys[i].time _start _end) then
			(
				selectKey obj.scale.controller i
			)
		)
		deleteKeys obj.scale.controller #selection
	)
)

fn A2UFn_PrintNodesTransformAtFrame nodes frametime = 
(
	at time frametime
	(
		--sliderTime = frametime
		print("@Frame " + frametime as string)
	
		for curNode in nodes do
		(
			if (isKindOf curNode Biped_Object) then
			(
				local str = curNode.name
				local _position = (in coordsys world Biped.getTransform curNode #pos) as string
				local _rotation = (quatToEuler(in coordsys world Biped.getTransform curNode #rotation)) as string
				local _scale = (in coordsys world Biped.getTransform curNode #scale) as string
				str = str + _position + _rotation + _scale
				print str
			)
			else
			(
				local str = curNode.name
				local _position = (curNode.transform.translationpart) as string
				local _rotation = (quatToEuler(curNode.transform.rotationpart)) as string
				local _scale = (curNode.transform.scalepart) as string
				str = str + _position + _rotation + _scale
				print str
			)
		)
	)
)

fn A2UFn_CheckHaveSameNameNodes nodes = 
(
	local NodeNames = #()
	local NodeSameNames = #()
	for curNode in nodes do
	(
		local curNodeName = curNode.name
		if findItem NodeNames curNodeName == 0 then
		(
			append NodeNames curNodeName
		)
		else
		(
			appendIfUnique NodeSameNames curNodeName
		)
	)
	return NodeSameNames
)

fn A2UFn_SelectSameNameNodes = 
(
	clearSelection()

	local NodeSameNames = A2UFn_CheckHaveSameNameNodes()
	for SameName in NodeSameNames do
	(
		
	)
)

fn A2UFn_ClearKeyFrames nodes = 
(
	local curSelection = getCurrentSelection()
	selectmore nodes
	macros.run "Animation Tools" "DeleteSelectedAnimation"
	clearSelection()
	selectmore curSelection
)

fn A2UFn_ClearAllKeyFrames = 
(
	local curSelection = getCurrentSelection()
	max select all
	macros.run "Animation Tools" "DeleteSelectedAnimation"
	clearSelection()
	selectmore curSelection
)

fn A2UFn_SetAnimRange start end = 
(
	animationRange = interval start end
)

fn A2UFn_GetMaxProgramSetting section key = 
(
	getINISetting A2UConfigPath section key
)

fn A2UFn_SetMaxProgramSetting section key value = 
(
	setINISetting A2UConfigPath section key value
)

fn Fn_SetDefaultFBXImportSetting mode = 
(
	if mode == "model" then
	(
		FBXImporterSetParam "Mode" #create
		FBXImporterSetParam "Animation" false
		FBXImporterSetParam "SmoothingGroups" true
		FBXImporterSetParam "Cameras" false
		FBXImporterSetParam "Lights" false
		FBXImporterSetParam "ScaleFactor" 1.0
	)
	else if mode == "anim" then
	(

	)
)

fn Fn_SetDefaultFBXExportSetting mode = 
(
	if mode == "model" then
	(
		FbxExporterSetParam "Animation" false

		fbxExporterSetParam "FileVersion" "FBX201600"
		fbxExporterSetParam "ASCII" false

		fbxExporterSetParam "SmoothingGroups" true
		fbxExporterSetParam "NormalsPerPoly" false
		fbxExporterSetParam "TangentSpaceExport" true
		fbxExporterSetParam "GeomAsBone" true
		fbxExporterSetParam "Triangulate" true
		fbxExporterSetParam "PreserveEdgeOrientation" true

		fbxExporterSetParam "ColladaTriangulate" false
		fbxExporterSetParam "ColladaSingleMatrix" false
		fbxExporterSetParam "ColladaFrameRate" 60.f

		fbxExporterSetParam "Cameras" false
		fbxExporterSetParam "Lights" false

		fbxExporterSetParam "EmbedTextures" false
		fbxExporterSetParam "UpAxis" "Y"

		fbxExporterSetParam "Convert2Tiff" false
		fbxExporterSetParam "ConvertUnit" "cm"
		fbxExporterSetParam "FilterKeyReducer" true
		fbxExporterSetParam "GenerateLog" false
		fbxExporterSetParam "PointCache" false
		fbxExporterSetParam "PreserveInstances" false
		fbxExporterSetParam "RemoveSingleKeys" true
		fbxExporterSetParam "ScaleFactor" 1.0
		fbxExporterSetParam "SelectionSetExport" false
		fbxExporterSetParam "ShowWarnings" true
		fbxExporterSetParam "SmoothMeshExport" false
		fbxExporterSetParam "UseSceneName" false
	)
	else if mode == "anim" then
	(
		
	)
)

fn Fn_ExportTxtFile exportPath exportFile exportString =
(
	local fullFilePathName = exportPath + "\\" + exportFile
	if makeDir exportPath all:true then
	(
		if doesFileExist (fullFilePathName) == true then
		(
			local curFile = openFile fullFilePathName mode:"w"
			if curFile != undefined then
			(
				format "%" exportString to:curFile
				close curFile
			)
		)
		else
		(
			local curFile = createFile fullFilePathName
			if curFile != undefined then
			(
				format "%" exportString to:curFile
				close curFile
			)
		)
	)
)